/****************************************************************************
 * Copyright (C) 2012
 * by Dj_Skual
 *
 * This software is provided 'as-is', without any express or implied
 * warranty. In no event will the authors be held liable for any
 * damages arising from the use of this software.
 *
 * Permission is granted to anyone to use this software for any
 * purpose, including commercial applications, and to alter it and
 * redistribute it freely, subject to the following restrictions:
 *
 * 1. The origin of this software must not be misrepresented; you
 * must not claim that you wrote the original software. If you use
 * this software in a product, an acknowledgment in the product
 * documentation would be appreciated but is not required.
 *
 * 2. Altered source versions must be plainly marked as such, and
 * must not be misrepresented as being the original software.
 *
 * 3. This notice may not be removed or altered from any source
 * distribution.
 *
 * gcbrowser.cpp
 *
 * for SaveGame Manager GX 2012
 ***************************************************************************/
#include <algorithm>
#include <wctype.h>

#include "../../XML/GameTDB.hpp"
#include "gcbrowser.h"

wchar_t MS_Latin1_Extended[0x20] = {
																	0x20ac, 0x0020,
	0x201a, 0x0020, 0x201e, 0x2026, 0x2020, 0x2021, 0x02c6, 0x2030, 0x0160, 0x2039,
	0x0152, 0x0020, 0x017d, 0x0020, 0x0020, 0x2018, 0x2019, 0x201c, 0x201d, 0x2022,
	0x2013, 0x2014, 0x02dc, 0x2122, 0x0161, 0x203a, 0x0153, 0x0020, 0x017e, 0x0178
};

GCBrowser::GCBrowser()
	:Browser()
{
	SearchChars.clear();
	
	//!Reset and prepare browser
	ResetBrowser();
	
	GetList();
}

GCBrowser::~GCBrowser()
{
	ResetBrowser();
}

void GCBrowser::ResetBrowser()
{
	for(u32 i = 0; i < browserList.size(); ++i)
		delete browserList.at(i);
	
	browserList.clear();
	FileFilter.clear();
	
	browserSelIndex = -1;
}

void GCBrowser::AddEntry()
{
	GCBrowserEntrieStruct * newSave = new GCBrowserEntrieStruct();
	//newSave->filenum = 0;
	memset(newSave->filename, 0x00, CARD_FILENAMELEN);
	newSave->name = L"";
	newSave->subname = L"";
	newSave->ID = "";
	newSave->permissions = 0;
	newSave->blocksize = 0;

	browserList.push_back(newSave);
}

void GCBrowser::SetSelectedIndex(int ind)
{
	if(browserSelIndex == ind)
		return;
	
	browserSelIndex = ind;
	IndexChanged();
}

/*void GCBrowser::RemoveEntry(int ind)
{
	if(ind < 0 || ind >= (int) EmuFiltered.size())
		return;
	
	EmuFiltered.erase(EmuFiltered.begin() + ind);
}*/

void GCBrowser::LoadTitlesFromGameTDB()
{
	if(!Settings.GameTDBPath || !Settings.GameTDBTitles)
		return;
	
	std::wstring Title;
	std::string gameTDBPath = Settings.GameTDBPath;
	gameTDBPath += "/wiitdb.xml";
	
	GameTDB * XML_DB = new GameTDB(gameTDBPath.c_str());
	XML_DB->SetLanguageCode(Settings.GameTDBLanguageCode);

	for(u32 i = 0; i < browserList.size(); ++i)
	{
		if(XML_DB->GetTitle(browserList.at(i)->ID.c_str(), Title))
			browserList.at(i)->name = Title;
	}
	
	delete XML_DB;
}

static bool NameSortCallback(const GCBrowserEntrieStruct *a, const GCBrowserEntrieStruct *b)
{
	return (stricmp((wstr2str(((GCBrowserEntrieStruct *)a)->name)).c_str(), (wstr2str(((GCBrowserEntrieStruct *)b)->name)).c_str()) < 0);
}

void GCBrowser::GetList()
{
	ResetBrowser();
	
	MemCardHandle * Card = NULL;
	curGcPath = Settings.GCUsedPath;
	if(!curGcPath.compare("gca"))
		Card = DeviceHandler::Instance()->GetGCAHandle();
	else
		Card = DeviceHandler::Instance()->GetGCBHandle();
	
	Card->GetList();
	
	int size = Card->GetListSize();
	for(int i = 0; i < size; ++i)
	{
		if(i >= (int)browserList.size())
			AddEntry();
		
		GCStruct * cardStruct = Card->GetEntry(i);
		
		//! filenum
		//browserList[i]->filenum = cardStruct->filenum;
		
		//! filename
		memcpy(&browserList[i]->filename, &cardStruct->filename, CARD_FILENAMELEN);
		
		//! ID
		char gamecode_str[5];
		memcpy(gamecode_str, &cardStruct->gamecode_str, 4);
		gamecode_str[4] = 0;
		browserList[i]->ID = gamecode_str;
		
		//! name
		int j = 0;
		for (j = 0; j < CARD_FILENAMELEN; j++)
		{
			if ((char)cardStruct->comments[j] == 0x00 ||
				(char)cardStruct->comments[j] == 0x0a)
				break;
			
			if ((char)cardStruct->comments[j] < 0x80 || 
				(char)cardStruct->comments[j] >= 0xa0)
			{
				browserList[i]->name.push_back((wchar_t)cardStruct->comments[j]);
			}
			else if((char)cardStruct->comments[j] >= 0x80 || 
					(char)cardStruct->comments[j] < 0xa0)
			{
				browserList[i]->name.push_back(MS_Latin1_Extended[cardStruct->comments[j] - 0x80]);
			}
		}
		while(!browserList[i]->name.compare(browserList[i]->name.size()-1, 1, L" "))
			browserList[i]->name.erase(browserList[i]->name.size()-1);
		
		//! subname
		for (j = CARD_FILENAMELEN; j < (CARD_FILENAMELEN*2); j++)
		{
			if ((char)cardStruct->comments[j] == 0x00 ||
				(char)cardStruct->comments[j] == 0x0a)
				break;
			
			if((char)cardStruct->comments[j] < 0x80 || 
				(char)cardStruct->comments[j] > 0x9f)
			{
				browserList[i]->subname.push_back((wchar_t)cardStruct->comments[j]);
			}
			else if((char)cardStruct->comments[j] >= 0x80 || 
					(char)cardStruct->comments[j] < 0xa0)
			{
				browserList[i]->subname.push_back(MS_Latin1_Extended[cardStruct->comments[j] - 0x80]);
			}
		}
		while(!browserList[i]->subname.compare(browserList[i]->subname.size()-1, 1, L" "))
			browserList[i]->subname.erase(browserList[i]->subname.size()-1);
		
		//! permissions
		browserList[i]->permissions = cardStruct->permissions;
		
		//! blocksize
		browserList[i]->blocksize = cardStruct->blocks;
	}
	
	Card->ResetList();
	
	//! set GameTDB Titles
	LoadTitlesFromGameTDB();
	
	if(browserList.size() > 1)
		std::sort(browserList.begin(), browserList.end(), NameSortCallback);
}

int GCBrowser::FilterList(std::wstring fileFilter)
{
	if(!fileFilter.empty() && (fileFilter.size() == FileFilter.size()))
		return browserList.size();
	
	if(fileFilter.size() < FileFilter.size())
		GetList();
	
	if(browserList.empty())
		return 0;
	
	if(!fileFilter.empty())
		FileFilter = fileFilter;
	else
		FileFilter.clear();

	SearchChars.clear();
	
	browserSelIndex = 0;
	
	for (u32 i = 0; i < browserList.size(); ++i)
	{
		std::wstring Filename = browserList.at(i)->name;
		if (Filename.empty())
		{
			browserList.erase(browserList.begin()+i);
			i--;
			continue;
		}
		
		if(Settings.SearchMode == SEARCH_BEGINNING)
		{
			if (FileFilter.size() > 0 && wcsncasecmp(Filename.c_str(), FileFilter.c_str(), FileFilter.size()) != 0)
			{
				browserList.erase(browserList.begin()+i);
				i--;
				continue;
			}
			
			if (Filename.size() > FileFilter.size() &&
				SearchChars.find(towupper(Filename[FileFilter.size()])) == SearchChars.end() &&
				SearchChars.find(towlower(Filename[FileFilter.size()])) == SearchChars.end())
			{
				SearchChars.insert(Filename[FileFilter.size()]);
			}
		}
		else if(Settings.SearchMode == SEARCH_CONTENT)
		{
			if(!FileFilter.empty())
			{
				if (wcscasestr(Filename.c_str(), FileFilter.c_str()) == 0)
				{
					browserList.erase(browserList.begin()+i);
					i--;
					continue;
				}
				
				const wchar_t *found = Filename.c_str();
				while((found = wcscasestr(found, FileFilter.c_str())) != 0)
				{
					found += FileFilter.size();
					wchar_t ch = towupper(*found);
					if(ch)
						SearchChars.insert(ch);
				}
			}
			else
			{
				for(const wchar_t *wPtr = Filename.c_str(); *wPtr != 0; wPtr++)
				{
					wchar_t ch = towupper(*wPtr);
					if(ch > L'@')
						SearchChars.insert(ch);
				}
			}
		}
	}

	if (browserList.size() < 2)
		SearchChars.clear();
	
	ListChanged();
	IndexChanged();
	
	return browserList.size();
}
